﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Input;
using TanmiaGrp.Basis.Core;
using TanmiaGrp.Basis;
using TanmiaGrp.GeneralLedger.WinApp.Commands;
using TanmiaGrp.Basis.Properties;
using System.ComponentModel;
using TanmiaGrp.GeneralLedger.Domain;

namespace TanmiaGrp.GeneralLedger.WinApp
{
    public class BaseEditorViewModel<TBusinessObject, TBusinessDomain> : BaseViewModel<TBusinessObject, TBusinessDomain>
        where TBusinessObject : BusinessObject, new()
        where TBusinessDomain : BaseDomain<TBusinessObject, TBusinessDomain>, new()
    {
        #region Private Members

        private TBusinessObject entity;
        private BrowserStateChangedEventArgs recordNavigationArgs;
        private string stateText;
        #endregion

        #region Public Properties

        /// <summary>
        /// Gets the command to invoke when (New) button is pressed.
        /// </summary>
        public ICommand NewCommand { get; protected set; }

        /// <summary>
        /// Gets the command to invoke when (Save) button is pressed.
        /// </summary>
        public ICommand SaveCommand { get; protected set; }

        /// <summary>
        /// Gets the command to invoke when (Save And New) button is pressed.
        /// </summary>
        public ICommand SaveAndNewCommand { get; protected set; }

        /// <summary>
        /// Gets the command to invoke when (Save And Close) button is pressed.
        /// </summary>
        public ICommand SaveAndCloseCommand { get; protected set; }

        /// <summary>
        /// Gets the command to invoke when (Delete) button is pressed.
        /// </summary>
        public ICommand DeleteCommand { get; protected set; }

        /// <summary>
        /// Represents Previous, and First entity navigation commands.
        /// </summary>
        public ICommand GoToForthEntityCommand { get; protected set; }

        /// <summary>
        /// Represents Next, and Last entity navigation commands.
        /// </summary>
        public ICommand GoToBackEntityCommand { get; protected set; }

        /// <summary>
        /// Gets or sets currently manipulated business entity.
        /// </summary>
        public TBusinessObject Entity
        {
            get { return entity; }
            set
            {
                if (entity != value)
                {
                    if (entity != null)
                        entity.PropertyChanged -= OnEntityPropertyChanged;

                    entity = value;

                    if (entity != null)
                        entity.PropertyChanged += OnEntityPropertyChanged;

                    RaisePropertyChanged(() => Entity);
                }
            }
        }

        /// <summary>
        /// Gets or sets the user message service.
        /// </summary>
        public IUserMessageService UserMessageSvc { get; set; }

        /// <summary>
        /// Gets or sets the progress service.
        /// </summary>
        public IProgress ProgressObj { get; set; }

        /// <summary>
        /// Caption of the state of current <see cref="Entity"/>.
        /// Edit, or Add.
        /// </summary>
        public string StateText
        {
            get { return stateText; }
            private set
            {
                if (stateText != value)
                {
                    stateText = value;
                    RaisePropertyChanged(() => StateText);
                }
            }
        }
        #endregion

        public BaseEditorViewModel()
        {
            ProgressObj = new MockProgress();
            UserMessageSvc = new UserMessageService();

            NewCommand = new DelegateCommand(OnNewHandler);
            SaveCommand = new DelegateCommand(OnSaveHandler, CanSaveHandler);
            SaveAndNewCommand = new DelegateCommand(OnSaveAndNewHandler, CanSaveHandler);
            SaveAndCloseCommand = new DelegateCommand<object>(OnSaveAndCloseHandler, CanSaveHandler);
            DeleteCommand = new DelegateCommand<object>(OnDeleteHandler, CanDeleteHandler);
            GoToForthEntityCommand = new DelegateCommand<NavigationMoveEnum?>(OnEntityNavigateHandler, CanEntityNavigateForthHandler);
            GoToBackEntityCommand = new DelegateCommand<NavigationMoveEnum?>(OnEntityNavigateHandler, CanEntityNavigateBackHandler);
        }

        protected override void Initialize()
        {
            PageNavigationEvent.Register<NavigationMoveEnum?>(OnEntityNavigateHandler);
            BrowserInitializedEvent.Register<BrowserStateChangedEventArgs>(OnBrowserViewModel_Initialized);
        }

        #region Core Logic
        /// <summary>
        /// Execute main flow of saving entity process.
        /// </summary>
        /// <returns></returns>
        protected virtual bool CoreSave()
        {
            bool retVal = false;

            Entity.Validate();

            if (Entity.IsValid)
            {
                if (Entity.IsEmpty)
                {
                    TBusinessObject addedEntity = BusinessDomain.Add(Entity);
                    if (addedEntity != null)
                    {
                        retVal = true;
                        Entity = addedEntity;
                    }
                }
                else
                {
                    Entity.LastUpdateDate = DateTime.Now;
                    TBusinessObject updatedEntity = BusinessDomain.Update(Entity);
                    if (updatedEntity != null)
                    {
                        retVal = true;
                        Entity = updatedEntity;
                    }
                }

                Entity.CaptureState();
            }
            else
                throw new PresentationException("يجب يتم التأكد بأن كافة قواعد التحقق من صحة البيانات صحيحة.");

            return retVal;
        }

        /// <summary>
        /// Execute main flow of deleting entity process.
        /// </summary>
        /// <returns></returns>
        protected virtual bool CoreDelete()
        {
            bool retVal = false;

            retVal = BusinessDomain.Delete(Entity);

            return retVal;
        }

        protected virtual TBusinessObject CoreNew()
        {
            Entity = new TBusinessObject();
            Entity.CaptureState();

            return Entity;
        }

        protected async virtual Task<bool> CoreSaveAsync()
        {
            return await Task.Factory.StartNew<bool>(() => CoreSave());
        }

        protected async virtual Task<bool> CoreDeleteAsync()
        {
            return await Task.Factory.StartNew<bool>(() => CoreDelete());
        }

        #endregion Core Logic

        #region Main Business Logic

        /// <summary>
        /// Create and populate UI's controls with new Business Entity.
        /// </summary>
        protected virtual void OnNewHandler()
        {
            if (PreEntityChange())
            {
                CoreNew();

                UpdateUI();

                UserMessageSvc.SendStatusMessage(BaseResources.New, DisplayEntityName);
            }
        }

        /// <summary>
        /// Determines if Save buttons can be pressed.
        /// </summary>
        /// <returns>Entity.IsValid value.</returns>
        protected virtual bool CanSaveHandler()
        {
            if (Entity == null)
                return false;
            else
                return Entity.IsValid;
        }

        /// <summary>
        /// Determines if Save buttons can be pressed.
        /// </summary>
        /// <returns>Entity.IsValid value.</returns>
        protected virtual bool CanSaveHandler(object sender)
        {
            if (Entity == null)
                return false;
            else
                return Entity.IsValid;
        }

        /// <summary>
        /// Save current entity to repository.
        /// </summary>
        protected async virtual void OnSaveHandler()
        {
            bool savingResult = false;

            if (ProgressObj.IsProgress)
                return;

            try
            {
                if (Entity == null)
                    throw new PresentationException("يجب إنشاء مجموعة جديد بالضغط على زر جديد قبل عملية الحفظ.");

                ProgressObj.ShowProgress("جاري حفظ المجموعة...");

                savingResult = await CoreSaveAsync();

                if (savingResult == false)
                    throw new PresentationException("حدث خطأ أثناء حفظ المجموعة.");

                OnEntitySavedOrDeleted(Entity);

                UpdateUI();
            }
            catch (PresentationException prEx)
            {
                UserMessageSvc.ShowInformationMessage(prEx);
            }
            catch (BaseAppException appEx)
            {
                UserMessageSvc.ShowErrorMessage(appEx);
            }

            UserMessageSvc.SendStatusMessage(savingResult, BaseResources.SaveSucceed, BaseResources.SaveUnsucceed, DisplayEntityName);

            ProgressObj.HideProgress();
        }

        /// <summary>
        /// Save current entity to repository and create new empty on UI.
        /// </summary>
        protected async virtual void OnSaveAndNewHandler()
        {
            bool savingResult = false;

            if (ProgressObj.IsProgress)
                return;

            ProgressObj.ShowProgress("جاري حفظ المجموعة وإنشاء مجموعة جديدة...");

            try
            {
                if (Entity == null)
                    throw new PresentationException("يجب إنشاء مجموعة جديد بالضغط على زر جديد قبل عملية الحفظ.");

                savingResult = await CoreSaveAsync();
                if (savingResult == false)
                    throw new PresentationException("حدث خطأ أثناء حفظ المجموعة.");

                // refresh BrowserViewModel must be done first before CoreNew() method
                // is called, because BrowserViewModel need to know the Entity to re-select
                // it after Refreshing itself.
                OnEntitySavedOrDeleted(Entity);

                CoreNew();

                UpdateUI();
            }
            catch (PresentationException prEx)
            {
                UserMessageSvc.ShowInformationMessage(prEx);
            }
            catch (BaseAppException appEx)
            {
                UserMessageSvc.ShowErrorMessage(appEx);
            }
            UserMessageSvc.SendStatusMessage(savingResult, BaseResources.SaveAndNewSucceed, BaseResources.SaveUnsucceed, DisplayEntityName);

            ProgressObj.HideProgress();
        }

        /// <summary>
        /// Save current entity to repository and close UI.
        /// </summary>
        protected async virtual void OnSaveAndCloseHandler(object sender)
        {
            bool savingResult = false;

            if (ProgressObj.IsProgress)
                return;

            ProgressObj.ShowProgress("جاري حفظ المجموعة وإنشاء مجموعة جديدة...");

            try
            {
                if (Entity == null)
                    throw new PresentationException("يجب إنشاء مجموعة جديد بالضغط على زر جديد قبل عملية الحفظ.");

                savingResult = await CoreSaveAsync();
                if (savingResult == false)
                    throw new PresentationException("حدث خطأ أثناء حفظ المجموعة.");

                OnEntitySavedOrDeleted(Entity);

                OnCloseHandler(sender);

                UpdateUI();
            }
            catch (PresentationException prEx)
            {
                UserMessageSvc.ShowInformationMessage(prEx);
            }
            catch (BaseAppException appEx)
            {
                UserMessageSvc.ShowErrorMessage(appEx);
            }
            UserMessageSvc.SendStatusMessage(savingResult, BaseResources.SaveAndNewSucceed, BaseResources.SaveUnsucceed, DisplayEntityName);

            ProgressObj.HideProgress();
        }

        /// <summary>
        /// Close current view.
        /// </summary>
        /// <param name="sender"></param>
        protected override void OnCloseHandler(object sender)
        {
            if (PreEntityChange())
            {
                Close(sender);

                Entity = null;

                UserMessageSvc.SendStatusMessage(BaseResources.CloseEditor, DisplayEntityName);
            }
        }

        /// <summary>
        /// Determine if Delete button can be pressed or not.
        /// </summary>
        /// <returns></returns>
        protected virtual bool CanDeleteHandler(object sender)
        {
            return Entity != null && !Entity.IsEmpty;
        }

        /// <summary>
        /// Called when Delete command is executed to delete current Entity from repository.
        /// </summary>
        /// <param name="sender">represent the Button that was pressed, used to figure out what is the View to close.</param>
        protected async virtual void OnDeleteHandler(object sender)
        {
            bool deletingResult = false;

            if (ProgressObj.IsProgress)
                return;

            try
            {
                if (Entity == null || Entity.IsEmpty)
                    throw new PresentationException("يجب أن يكون هناك مجموعة حتى يتم حذفها.");

                MessageBoxResult userResponse = UserMessageSvc.RequestUserConfirmation("هل أنت متأكد من حذف المجموعة المحددة؟", "تأكيد حذف");
                if (userResponse == MessageBoxResult.Yes)
                {
                    ProgressObj.ShowProgress("جاري حذف المجموعة...");

                    deletingResult = await CoreDeleteAsync();

                    if (deletingResult == false)
                        throw new PresentationException("حدث خطأ أثناء حذف المجموعة.");

                    Entity = null;

                    OnEntitySavedOrDeleted(Entity);

                    UpdateUI();

                    OnCloseHandler(sender);
                }
            }
            catch (PresentationException prEx)
            {
                UserMessageSvc.ShowInformationMessage(prEx);
            }
            catch (BaseAppException appEx)
            {
                UserMessageSvc.ShowErrorMessage(appEx);
            }

            UserMessageSvc.SendStatusMessage(deletingResult, BaseResources.DeleteSucceed, BaseResources.DeleteUnsucceed, 1, DisplayEntityName);

            ProgressObj.HideProgress();
        }

        /// <summary>
        /// Determines if entity navigation can go Last, or Next Entity.
        /// </summary>
        /// <returns></returns>
        protected virtual bool CanEntityNavigateBackHandler(NavigationMoveEnum? arg)
        {
            if (recordNavigationArgs == null) return false;

            return !recordNavigationArgs.IsAtLast;
        }

        /// <summary>
        /// Determines if entity navigation can go First, or Previous Entity.
        /// </summary>
        /// <returns></returns>
        protected virtual bool CanEntityNavigateForthHandler(NavigationMoveEnum? arg)
        {
            if (recordNavigationArgs == null) return false;

            return !recordNavigationArgs.IsAtFirst;
        }

        /// <summary>
        /// Called when EntityNavigationCommands or PageNavigationCommands are executed.
        /// </summary>
        /// <param name="obj"></param>
        protected virtual void OnEntityNavigateHandler(NavigationMoveEnum? obj)
        {
            if (PreEntityChange())
            {
                // fire event to request group Browser to navigate to other record.
                recordNavigationArgs = OnEntityNavigation(obj);

                if (recordNavigationArgs != null)
                    Entity = recordNavigationArgs.GetCurrentEntity<TBusinessObject>();

                PostEntityChange();

                UserMessageSvc.SendStatusMessage("Moved " + obj.ToString(), DisplayEntityName);
            }
        }
        #endregion Main Business Logic

        #region Event Handlers

        /// <summary>
        /// Called when BrowserViewModel opened, and Initialized its data.
        /// </summary>
        /// <param name="args"></param>
        protected virtual void OnBrowserViewModel_Initialized(BrowserStateChangedEventArgs args)
        {
            recordNavigationArgs = args;
            UpdateUI();
        }

        /// <summary>
        /// Called when any property of Business Entity changed.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        protected virtual void OnEntityPropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            UpdateUI();
        }

        #endregion

        #region Helper Methods

        /// <summary>
        /// True, when user decide to cancel the leaving of current entity.
        /// </summary>
        /// <returns>True, if user pressed Yes, or No. False, if user pressed Cancel.</returns>
        protected virtual bool PreEntityChange()
        {
            bool retVal = false;

            if (Entity != null && Entity.IsStateChanged)
            {
                MessageBoxResult result = MessageBox.Show("حفظ التعديلات؟", "تأكيد حفظ", MessageBoxButton.YesNoCancel, MessageBoxImage.Question);
                if (result == MessageBoxResult.Yes)
                {
                    OnSaveHandler();
                    retVal = true;
                }
                else if (result == MessageBoxResult.No)
                {
                    OnEntitySavedOrDeleted(Entity);
                    retVal = true;
                }
                else
                    retVal = false;
            }
            else
                retVal = true;

            return retVal;
        }

        protected virtual void PostEntityChange()
        {
            if (Entity != null)
                Entity.CaptureState();

            UpdateUI();
        }

        /// <summary>
        /// Causes the UI elements to refresh their status.
        /// </summary>
        protected virtual void UpdateUI()
        {
            SaveCommand.RaiseCanExecuteChanged();
            SaveAndNewCommand.RaiseCanExecuteChanged();
            SaveAndCloseCommand.RaiseCanExecuteChanged();
            GoToBackEntityCommand.RaiseCanExecuteChanged();
            GoToForthEntityCommand.RaiseCanExecuteChanged();
            DeleteCommand.RaiseCanExecuteChanged();

            // because may be the task that load Editor Control didn't finished yet
            // so OnNavigatedTo() had not been called yet
            // so Entity = null.
            if (Entity != null)
                StateText = Entity.IsEmpty ? "اضافة" : "تعديل";
        }

        #endregion
    }
}
